package com.morgan.recyclerviewdemo.itemdecoration

import android.content.Context
import android.graphics.*
import android.graphics.drawable.Drawable
import android.view.View
import androidx.annotation.ColorInt
import androidx.recyclerview.widget.RecyclerView
import com.morgan.recyclerviewdemo.utils.DeviceUtil

/**
 * 时间轴
 */
class TimeLineItemDecoration<T : TimeLineItemDecoration.ITimeText>(context : Context, var mDatas : List<T>) : RecyclerView.ItemDecoration(){

    //圆圈
    private var mCircleDrawable: Drawable? = null
    @ColorInt var mCircleSideColor : Int = Color.BLUE
    @ColorInt var mCircleSolidColor : Int = Color.GREEN
    var mCircleRadius : Int = DeviceUtil.dp2px(context, 5f)

    //时间线
    var mLineWidth : Int = DeviceUtil.dp2px(context, 2f)
    var mLineColor : Int = Color.YELLOW

    //左边区域
    var mLeftTimeRegionWidth : Int = DeviceUtil.dp2px(context, 80f)

    var mTimeTextLeftMargin : Int = DeviceUtil.dp2px(context, 10f)
    var mTimeLineRightMargin : Int = DeviceUtil.dp2px(context, 10f)

    companion object {
        val DIRECTION_TOP = 1
        val DIRECTION_ALIGN_ITEM = 2
        val DIRECTION_BOTTOM = 3
    }

    //左边时间布局相对于RecyclerView.ItemView的方向
    var mTimeTextDirection : Int = DIRECTION_ALIGN_ITEM
    private val mPaint : Paint = Paint(Paint.ANTI_ALIAS_FLAG)
    //左边时间布局的文本上下之间的距离
    var mTimeTextItemMargin : Int = DeviceUtil.dp2px(context, 5f)

    //左边时间布局相对于RecyclerView.ItemView的方向顶部时与上边的距离
    var mTimeTextDirectionTopMargin = DeviceUtil.dp2px(context, 2f)
    //左边时间布局相对于RecyclerView.ItemView的方向底部时与下边的距离
    var mTimeTextDirectionBottomMargin = DeviceUtil.dp2px(context, 2f)

    //recyclerview头部view的个数
    var mHeaderCount = 0
    //recyclerview尾部view的个数
    var mFooterCount = 0

    private val mRect : Rect = Rect()


    interface ITimeText {
        fun getTimeDesc() : IText
        fun getDateDesc() : IText
        fun getCircleDrawble() : Drawable?
    }

    interface IText {
        fun getText() : String
        fun getTextSize() : Float
        fun getTextColor() : Int
    }


    fun setCircleDrawable(drawable: Drawable) {
        mCircleDrawable = drawable
    }

    override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State
    ) {
        if (mLeftTimeRegionWidth <= 0 || mDatas.isEmpty()) {
            super.getItemOffsets(outRect, view, parent, state)
            return
        }
        val item = mDatas[0]

        val timeDesc = item.getTimeDesc()
        mPaint.textSize = timeDesc.getTextSize()
//        val timeFontMetrics = mPaint.fontMetrics
//        val timeHeight = timeFontMetrics.descent - timeFontMetrics.ascent
        mPaint.getTextBounds(timeDesc.getText(), 0, timeDesc.getText().length, mRect)
        val timeHeight = mRect.height().toFloat()

        val dateDesc = item.getDateDesc()
        mPaint.textSize = dateDesc.getTextSize()
//        val dateFontMetrics = mPaint.fontMetrics
//        val dateHeight = dateFontMetrics.descent - dateFontMetrics.ascent
        mPaint.getTextBounds(dateDesc.getText(), 0, dateDesc.getText().length, mRect)
        val dateHeight = mRect.height().toFloat()

        var left = mLeftTimeRegionWidth
        var top = 0
        var right = 0
        var bottom = 0

        val childHeight = view.measuredHeight
        when (mTimeTextDirection) {
            DIRECTION_TOP -> {
                top = (mTimeTextDirectionTopMargin + timeHeight + mTimeTextItemMargin + dateHeight).toInt()
            }
            DIRECTION_ALIGN_ITEM -> {
                val timeLineRegionHeight = (timeHeight + mTimeTextItemMargin + dateHeight).toInt()
                if (timeLineRegionHeight > childHeight) {
                    top = (timeLineRegionHeight - childHeight)/2
                    bottom = top
                }
            }
            DIRECTION_BOTTOM -> {
                bottom = (timeHeight + mTimeTextItemMargin + dateHeight + mTimeTextDirectionBottomMargin).toInt()
            }
        }
        outRect.set(left, top, right, bottom)
    }



    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        if (mLeftTimeRegionWidth <= 0 || mDatas.isEmpty() || parent.adapter == null) {
            return
        }
        val itemCount = parent.adapter!!.itemCount
        if (itemCount == 0) {
            return
        }
        val rang = 0..itemCount - 1
        for (i in rang) {
            if (i < mHeaderCount) {//时间轴不需要画头部布局
                continue
            }
            if (i >= itemCount - mFooterCount) {//时间轴不需要画尾部布局
                continue
            }
            val child = parent.getChildAt(i)
            val childPos = parent.getChildAdapterPosition(child)
            if (childPos == RecyclerView.NO_POSITION) {
                continue
            }
            drawTimeView(c, parent, state, child, mDatas[childPos])
        }
    }

    private fun drawTimeView(c: Canvas, parent: RecyclerView, state: RecyclerView.State, child : View, item: T) {
        when (mTimeTextDirection) {
            DIRECTION_TOP -> {
                drawTopTimeView(c, parent, state, child, item)
            }
            DIRECTION_ALIGN_ITEM -> {
                drawCenterTimeView(c, parent, state, child, item)
            }
            DIRECTION_BOTTOM -> {
                drawBottomTimeView(c, parent, state, child, item)
            }
        }
    }

    private fun drawBottomTimeView(
        c: Canvas,
        parent: RecyclerView,
        state: RecyclerView.State,
        child: View,
        item: T
    ) {
        mPaint.reset()
        mPaint.flags = Paint.ANTI_ALIAS_FLAG

        val parentPaddingLeft = parent.paddingLeft
        val childTop = child.top
        val childBottom = child.bottom

        val childLeft = child.left
        val childTranslationX = child.translationX
        val childTranslationY = child.translationY


        //draw time text
        val dateDesc = item.getDateDesc()
        mPaint.textSize = dateDesc.getTextSize()
        mPaint.color = dateDesc.getTextColor()
//        val dateFontMetrics = mPaint.fontMetrics
//       val dateHeight =  dateFontMetrics.descent - dateFontMetrics.ascent
        mPaint.getTextBounds(dateDesc.getText(), 0, dateDesc.getText().length, mRect)
        var dateHeight = mRect.height().toFloat()

        val timeDesc = item.getTimeDesc()
        mPaint.textSize = timeDesc.getTextSize()
        mPaint.color = timeDesc.getTextColor()
//        val timeFontMetrics = mPaint.fontMetrics
//        val timeHeight = timeFontMetrics.descent - timeFontMetrics.ascent
        mPaint.getTextBounds(timeDesc.getText(), 0, timeDesc.getText().length, mRect)
        var timeHeight = mRect.height().toFloat()

        c.drawText(timeDesc.getText(), parentPaddingLeft + mTimeTextLeftMargin + childTranslationX, childBottom + timeHeight + childTranslationY,  mPaint)

        //draw date text
        mPaint.textSize = dateDesc.getTextSize()
        mPaint.color = dateDesc.getTextColor()
        c.drawText(dateDesc.getText(), parentPaddingLeft + mTimeTextLeftMargin + childTranslationX, childBottom +  timeHeight + mTimeTextItemMargin + dateHeight + childTranslationY, mPaint)

        mPaint.textSize = timeDesc.getTextSize()
        val timeFontMetrics = mPaint.fontMetrics
        timeHeight = timeFontMetrics.descent - timeFontMetrics.ascent
        mPaint.textSize = dateDesc.getTextSize()
        val dateFontMetrics = mPaint.fontMetrics
        dateHeight =  dateFontMetrics.descent - dateFontMetrics.ascent

        drawCircleAndLine(c, child, timeHeight, dateHeight, item)
    }

    private fun drawCenterTimeView(
        c: Canvas,
        parent: RecyclerView,
        state: RecyclerView.State,
        child: View,
        item: T
    ) {
        mPaint.reset()
        mPaint.flags = Paint.ANTI_ALIAS_FLAG

        val parentPaddingLeft = parent.paddingLeft
        val childTop = child.top
        val childBottom = child.bottom

        val childLeft = child.left
        val childTranslationX = child.translationX
        val childTranslationY = child.translationY


        //draw time text
        val dateDesc = item.getDateDesc()
        mPaint.textSize = dateDesc.getTextSize()
        mPaint.color = dateDesc.getTextColor()
//        val dateFontMetrics = mPaint.fontMetrics
//        val dateHeight =  dateFontMetrics.descent - dateFontMetrics.ascent
        mPaint.getTextBounds(dateDesc.getText(), 0, dateDesc.getText().length, mRect)
        var dateHeight = mRect.height().toFloat()

        val timeDesc = item.getTimeDesc()
        mPaint.textSize = timeDesc.getTextSize()
        mPaint.color = timeDesc.getTextColor()
//        val timeFontMetrics = mPaint.fontMetrics
//        val timeHeight = timeFontMetrics.descent - timeFontMetrics.ascent
        mPaint.getTextBounds(timeDesc.getText(), 0, timeDesc.getText().length, mRect)
        var timeHeight = mRect.height().toFloat()

        c.drawText(timeDesc.getText(), parentPaddingLeft + mTimeTextLeftMargin + childTranslationX, childTop + timeHeight + childTranslationY,  mPaint)

        //draw date text
        mPaint.textSize = dateDesc.getTextSize()
        mPaint.color = dateDesc.getTextColor()
        c.drawText(dateDesc.getText(), parentPaddingLeft + mTimeTextLeftMargin + childTranslationX, childTop + timeHeight + mTimeTextItemMargin + dateHeight + childTranslationY, mPaint)


        mPaint.textSize = timeDesc.getTextSize()
        val timeFontMetrics = mPaint.fontMetrics
        timeHeight = timeFontMetrics.descent - timeFontMetrics.ascent

        mPaint.textSize = dateDesc.getTextSize()
        val dateFontMetrics = mPaint.fontMetrics
        dateHeight =  dateFontMetrics.descent - dateFontMetrics.ascent
        drawCircleAndLine(c, child, timeHeight, dateHeight, item)
    }

    private fun drawTopTimeView(
        c: Canvas,
        parent: RecyclerView,
        state: RecyclerView.State,
        child: View,
        item: T
    ) {

        mPaint.reset()
        mPaint.flags = Paint.ANTI_ALIAS_FLAG

        val parentPaddingLeft = parent.paddingLeft
        val childTop = child.top
        val childBottom = child.bottom

        val childLeft = child.left
        val childTranslationX = child.translationX
        val childTranslationY = child.translationY


        //draw time text
        val dateDesc = item.getDateDesc()
        mPaint.textSize = dateDesc.getTextSize()
        mPaint.color = dateDesc.getTextColor()
//        val dateFontMetrics = mPaint.fontMetrics
//       val dateHeight =  dateFontMetrics.descent - dateFontMetrics.ascent
        mPaint.getTextBounds(dateDesc.getText(), 0, dateDesc.getText().length, mRect)
        var dateHeight = mRect.height().toFloat()

        val timeDesc = item.getTimeDesc()
        mPaint.textSize = timeDesc.getTextSize()
        mPaint.color = timeDesc.getTextColor()
//        val timeFontMetrics = mPaint.fontMetrics
//        val timeHeight = timeFontMetrics.descent - timeFontMetrics.ascent
        mPaint.getTextBounds(timeDesc.getText(), 0, timeDesc.getText().length, mRect)
        var timeHeight = mRect.height().toFloat()

        c.drawText(timeDesc.getText(), parentPaddingLeft + mTimeTextLeftMargin + childTranslationX, childTop - dateHeight -  mTimeTextItemMargin + childTranslationY,  mPaint)

        //draw date text
        mPaint.textSize = dateDesc.getTextSize()
        mPaint.color = dateDesc.getTextColor()
        c.drawText(dateDesc.getText(), parentPaddingLeft + mTimeTextLeftMargin + childTranslationX, childTop + childTranslationY, mPaint)

//        mPaint.textSize = timeDesc.getTextSize()
//        val timeFontMetrics = mPaint.fontMetrics
//        timeHeight = timeFontMetrics.descent - timeFontMetrics.ascent
//        mPaint.textSize = dateDesc.getTextSize()
//        val dateFontMetrics = mPaint.fontMetrics
//        dateHeight =  dateFontMetrics.descent - dateFontMetrics.ascent

       drawCircleAndLine(c, child, timeHeight, dateHeight, item)
    }

    private fun drawCircleAndLine(c: Canvas, child: View, timeHeight : Float, dateHeight : Float, item: T) {
        val childTop = child.top
        val childBottom = child.bottom

        val childLeft = child.left
        val childTranslationX = child.translationX
        val childTranslationY = child.translationY
        val childHeight = child.measuredHeight

        val circleCenterX = childLeft - mTimeLineRightMargin + childTranslationX
        val circleCenterY = childTop + mCircleRadius + childTranslationY

        //draw vertical line
        val lineLeft = (circleCenterX - mLineWidth/2 + childTranslationX).toInt()
//        val lineTop = childTop - (dateHeight + mTimeTextItemMargin + timeHeight + mTimeTextDirectionTopMargin) + childTranslationY
        var lineTop = 0f
//        var lineBottom = childBottom + childTranslationY
        var lineBottom = 0f
        when (mTimeTextDirection) {
            DIRECTION_TOP -> {
//                lineTop = childTop - dateHeight - mTimeTextItemMargin - timeHeight - mTimeTextDirectionTopMargin + childTranslationY
                lineTop = childTop - (mTimeTextDirectionTopMargin + timeHeight + mTimeTextItemMargin + dateHeight) + childTranslationY
                lineBottom = childBottom + childTranslationY
            }
            DIRECTION_ALIGN_ITEM -> {
                val timeLineRegionHeight = (timeHeight + mTimeTextItemMargin + dateHeight).toInt()
                if (timeLineRegionHeight > childHeight) {
                    lineTop = childTop - (timeLineRegionHeight - childHeight)/2 + childTranslationY
//                    lineBottom = childBottom + (timeLineRegionHeight - childHeight)/2 + childTranslationY
                    lineBottom = childTop + timeHeight + mTimeTextItemMargin + dateHeight + childTranslationY
                } else {
                    lineTop = childTop + childTranslationY
                    lineBottom = childBottom + childTranslationY
                }
            }
            DIRECTION_BOTTOM -> {
                lineTop = childTop.toFloat()
                lineBottom = childBottom +  timeHeight + mTimeTextItemMargin + dateHeight + childTranslationY
            }
        }

        val lineRight = lineLeft + mLineWidth

        mRect.set(lineLeft, lineTop.toInt() , lineRight, lineBottom.toInt())
        mPaint.color = mLineColor
        c.drawRect(mRect, mPaint)

        if (item.getCircleDrawble() != null) {
            drawCircleByDrawable(c, circleCenterX, circleCenterY, mCircleRadius.toFloat(), item.getCircleDrawble()!!)
            return
        }

        if (mCircleDrawable != null) {
            drawCircleByDrawable(c, circleCenterX, circleCenterY, mCircleRadius.toFloat(), mCircleDrawable!!)
            return
        }
        //draw solid circle
        mPaint.color = mCircleSolidColor
        mPaint.style = Paint.Style.FILL
        c.drawCircle(circleCenterX, circleCenterY, mCircleRadius.toFloat(), mPaint)
        //draw side circle
        mPaint.color = mCircleSideColor
        mPaint.style = Paint.Style.STROKE
        c.drawCircle(circleCenterX, circleCenterY, mCircleRadius.toFloat(), mPaint)
    }

    private fun drawCircleByDrawable(
        c: Canvas,
        circleCenterX: Float,
        circleCenterY: Float,
        radius : Float,
        circleDrawable : Drawable
    ) {
        val thumeLeft = (circleCenterX - radius).toInt()
        val thumeTop = (circleCenterY - radius).toInt()
        val thumeRight = (circleCenterX + radius).toInt()
        val thumeBottom = (circleCenterY + radius).toInt()
        mRect.set(thumeLeft, thumeTop, thumeRight, thumeBottom)
        circleDrawable.bounds = mRect
        circleDrawable.draw(c)
    }
}